#include <stdio.h>
#include <limits.h>

#define V 5  // 定义图中的顶点数

// 一个实用的函数来找到还没有包含在MST中，且键值最小的顶点
int minKey(int key[], int mstSet[]) {
    int min = INT_MAX, min_index;
    for (int v = 0; v < V; v++) {
        if (mstSet[v] == 0 && key[v] < min) {
            min = key[v];
            min_index = v;
        }
    }
    return min_index;
}

// 一个实用的函数来打印构造的MST存储在parent[]数组中
void printMST(int parent[], int graph[V][V]) {
    printf("Edge \tWeight\n");
    for (int i = 1; i < V; i++) {
        printf("%d - %d \t%d \n", parent[i], i, graph[i][parent[i]]);
    }
}

// 函数来构造和打印MST，图使用邻接矩阵表示法
void primMST(int graph[V][V]) {
    int parent[V];  // 存储构造的MST的数组
    int key[V];     // 键值，用于选择最小权重边中的最小键值
    int mstSet[V];  // 表示已经包含在MST中的顶点集

    // 初始化所有键值为无穷大，mstSet[]为false
    for (int i = 0; i < V; i++) {
        key[i] = INT_MAX, mstSet[i] = 0;
    }

    // 总是先包括第一个第一个顶点在MST中。
    // 使key为0，这样这个顶点就是第一个被选中的顶点。
    key[0] = 0;
    parent[0] = -1; // 第一个顶点总是MST的根

    // MST将有V个顶点
    for (int count = 0; count < V - 1; count++) {
        // 选出一个没有被包含在MST中的顶点，且键值最小的顶点
        int u = minKey(key, mstSet);

        // 把这个顶点加入到mstSet中
        mstSet[u] = 1;

        // 更新adj[v]和key[v]
        for (int v = 0; v < V; v++) {
            // graph[u][v]是非零则只有当v不在mstSet中，且graph[u][v]小于key[v]时
            if (graph[u][v] && mstSet[v] == 0 && graph[u][v] < key[v]) {
                parent[v] = u, key[v] = graph[u][v];
            }
        }
    }

    // 打印构造的MST
    printMST(parent, graph);
}

// 测试以上函数
int main() {
    /* 创建以下的图的邻接矩阵表示
           2    3
       (0)--(1)--(2)
        |   / \   |
       6| 8/   \5 |7
        | /     \ |
       (3)-------(4)
            9          
    */
    int graph[V][V] = {{0, 2, 0, 6, 0},
                       {2, 0, 3, 8, 5},
                       {0, 3, 0, 0, 7},
                       {6, 8, 0, 0, 9},
                       {0, 5, 7, 9, 0}};

    // 打印生成的最小生成树
    primMST(graph);

    return 0;
}
